#pragma once

#include "zen_app.h"
#include "zen_color.h"
#include "zen_matrix.h"

namespace Zen
{
	namespace Graphics
	{
		enum class EBlend : uint8_t
		{
			None,
			Normal,
			Addition,
			Custom,
			End_,
			Begin_ = 0,
		};

		enum class EFace : uint8_t
		{
			None,
			Front,
			Back,
			Both,
			End_,
			Begin_ = 0,
		};

		enum class EDrawMode : uint8_t
		{
			Point,
			Line,
			LineLoop,
			LineStrip,
			Triangle,
			TriangleStrip,
			End_,
			Begin_ = 0,
		};

		enum class EMinMagFilter : uint8_t
		{
			Nearest,
			Linear,
			End_,
			Begin_ = 0,
		};

		enum class EMipFilter : uint8_t
		{
			NotMapped,
			Nearest,
			Linear,
			End_,
			Begin_ = 0,
		};

		enum class EWrapMode : uint8_t
		{
			ClampToEdge,
			MirrorClampToEdge,
			Repeat,
			MirrorRepeat,
			End_,
			Begin_ = 0,
		};

		struct SamplerMode
		{
			EMinMagFilter minFilter{ EMinMagFilter::Nearest };
			EMinMagFilter magFilter{ EMinMagFilter::Nearest };
			//EMipFilter mipFilter{ EMipFilter::NotMapped };
			EWrapMode swrap{ EWrapMode::ClampToEdge };
			EWrapMode twrap{ EWrapMode::ClampToEdge };
		};
		class Texture
		{
		public:
			virtual float GetWidth() const = 0;
			virtual float GetHeight() const = 0;
			virtual EPixel GetPixelType() const = 0;
		};

		class Buffer
		{
		public:
			virtual size_t GetByteCount() const = 0;
			virtual void Initialize(size_t byteCount, void const* data) = 0;
			virtual void Update(size_t offsetInByte, size_t subByteCount, void const* data) = 0;
		};

		class IndexBuffer
		{
		public:
			virtual size_t GetCount() const = 0;
			virtual int GetElementSize() const = 0;
			virtual void Initialize(size_t count, uint32_t const* data) = 0;
			virtual void Update(size_t offset, size_t subCount, uint32_t const* data) = 0;
			virtual void Initialize(size_t count, uint16_t const* data) = 0;
			virtual void Update(size_t offset, size_t subCount, uint16_t const* data) = 0;
		};

		class Program
		{
		public:
			virtual void Draw(EDrawMode mode, size_t offset, size_t count, EBlend blend, EFace culled) = 0;

			virtual void DrawIndex(EDrawMode mode, IndexBuffer* buffer, size_t offset, size_t count, EBlend blend, EFace culled) = 0;

			virtual ~Program() = default;
		};

		class ProgramUniformColor : public Program
		{
		public:
			virtual void Attach(Buffer* vertex, int dimension, Mat4 const* matrix, Color4f const* ucolor) = 0;
		};

		class ProgramVaryingColor : public Program
		{
		public:
			virtual void Attach(Buffer* vertex, int dimension, Mat4 const* matrix, Buffer* vcolor, Color4f const* ucolor) = 0;
		};

		class ProgramTexture : public Program
		{
		public:
			virtual void Attach(Buffer* vertex, int dimension, Mat4 const* matrix, Buffer* coords, Texture* texture, SamplerMode const* sampler, Color4f const* ucolor) = 0;
		};

		class ProgramPointTexture : public Program
		{
		public:
			virtual void Attach(Buffer* vertex, int dimension, Mat4 const* matrix, Buffer* colors, Buffer* sizes, Texture* texture, SamplerMode const* sampler, Color4f const* ucolor) = 0;
		};

		class Engine
		{
		public:
			static std::shared_ptr<Engine> GLES();
			static std::shared_ptr<Engine> Metal();

			virtual void Initialize() = 0;

			virtual void SetClockwiseAsFront(bool clockwise) = 0;
			virtual void SetClearColor(Color4f color) = 0;
			virtual void BeginRender() = 0;
			virtual void EndRender() = 0;

			virtual std::shared_ptr<Texture> CreateTexture(EPixel bpp, size_t width, size_t height, void const* data) = 0;
			virtual std::shared_ptr<Buffer> GenerateBuffer() = 0;
			virtual std::shared_ptr<IndexBuffer> GenerateIndexBuffer() = 0;

			virtual std::shared_ptr<ProgramUniformColor> CreateProgramUniformColor(std::string const& vshader, std::string const& fshader) = 0;
			virtual std::shared_ptr<ProgramUniformColor> DefaultProgramUniformColor() = 0;

			virtual std::shared_ptr<ProgramVaryingColor> CreateProgramVaryingColor(std::string const& vshader, std::string const& fshader) = 0;
			virtual std::shared_ptr<ProgramVaryingColor> DefaultProgramVaryingColor() = 0;

			virtual std::shared_ptr<ProgramTexture> CreateProgramTexture(std::string const& vshader, std::string const& fshader) = 0;
			virtual std::shared_ptr<ProgramTexture> DefaultProgramTexture() = 0;
			virtual std::shared_ptr<ProgramTexture> CreateProgramTextureA(std::string const& vshader, std::string const& fshader) = 0;
			virtual std::shared_ptr<ProgramTexture> DefaultProgramTextureA() = 0;

			virtual std::shared_ptr<ProgramPointTexture> CreateProgramPointTexture(std::string const& vshader, std::string const& fshader) = 0;
			virtual std::shared_ptr<ProgramPointTexture> DefaultProgramPointTexture() = 0;
			virtual std::shared_ptr<ProgramPointTexture> CreateProgramPointTextureA(std::string const& vshader, std::string const& fshader) = 0;
			virtual std::shared_ptr<ProgramPointTexture> DefaultProgramPointTextureA() = 0;
		};
	} // namespace Graphics
} // namespace Zen
